#include "util.hpp"
#include "Protocol.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "daemonize.hpp"
#include <cerrno>

class ServerTcp; // 声明一下ServerTcp

class ThreadData
{
public:
    uint16_t clientPort_;
    std::string clinetIp_;
    int sock_;
    ServerTcp *this_;

public:
    ThreadData(uint16_t port, std::string ip, int sock, ServerTcp *ts)
        : clientPort_(port), clinetIp_(ip), sock_(sock), this_(ts)
    {
    }
};

// 大小写转换服务
// 我们认为我们读到的都是字符串
//  TCP && UDP 支持全双工: 使用一个sockfd来读写
void transService(int sock, const std::string &clientIp, uint16_t clientPort)
{
    assert(sock >= 0);
    assert(!clientIp.empty());
    assert(clientPort >= 1024);
    char inbuffer[BUFFER_SIZE];
    while (true)
    {
        ssize_t s = read(sock, inbuffer, sizeof(inbuffer) - 1);
        if (s > 0)
        {
            // read success
            inbuffer[s] = '\0';
            // strcasecmp -- 忽略大小写的比较
            if (strcasecmp(inbuffer, "quit") == 0)
            {
                logMessage(DEBUG, "client quit -- %s[%d]", clientIp.c_str(), clientPort);
                break;
            }
            logMessage(DEBUG, "trans before: %s[%d] >>> %s", clientIp.c_str(), clientPort, inbuffer);
            // 可以进行大小写转换了
            for (int i = 0; i < s; ++i)
            {
                if (isalpha(inbuffer[i]) && islower(inbuffer[i]))
                    inbuffer[i] = toupper(inbuffer[i]);
            }
            logMessage(DEBUG, "trans after: %s[%d] >>> %s", clientIp.c_str(), clientPort, inbuffer);

            write(sock, inbuffer, strlen(inbuffer));
        }
        else if (s == 0)
        {
            // pipe:读端一直读,写端不写了,读端会如何?
            logMessage(DEBUG, "client quit -- %s[%d]", clientIp.c_str(), clientPort);
            break;
        }
        else
        {
            logMessage(DEBUG, "%s[%d] -- read:%s", clientIp.c_str(), clientPort, strerror(errno));
            break;
        }
    }

    // 走到这里 一定是client退出 服务到此结束
    close(sock); // 如果一个进程文件fd打开了没有被归还 这种现象叫文件描述符泄漏!!
}

void execCommand(int sock, const std::string &clientIp, uint16_t clientPort)
{
    assert(sock >= 0);
    assert(!clientIp.empty());
    assert(clientPort >= 1024);
    char command[BUFFER_SIZE];

    while (true)
    {
        ssize_t s = read(sock, command, sizeof(command) - 1);
        if (s > 0)
        {
            command[s] = '\0';
            logMessage(DEBUG, "[%s:%d] exec [%s]", clientIp.c_str(), clientPort, command);
            FILE *fp = popen(command, "r");
            if (fp == nullptr)
            {
                logMessage(WARINING, "exec %s failed,because %s", command, strerror(errno));
            }
            char line[1024];
            while (fgets(line, sizeof(line) - 1, fp) != nullptr)
            {
                write(sock, line, strlen(line));
            }

            // dup2(sock,fp->_fileno);
            // fflush(fp);
            fclose(fp);
            logMessage(DEBUG, "[%s:%d] exec [%s] ... done", clientIp.c_str(), clientPort, command);
        }
        else if (s == 0)
        {
            // pipe:读端一直读,写端不写了,读端会如何?
            logMessage(DEBUG, "client quit -- %s[%d]", clientIp.c_str(), clientPort);
            break;
        }
        else
        {
            logMessage(DEBUG, "%s[%d] -- read:%s", clientIp.c_str(), clientPort, strerror(errno));
            break;
        }
    }

    // 走到这里 一定是client退出 服务到此结束
    close(sock); // 如果一个进程文件fd打开了没有被归还 这种现象叫文件描述符泄漏!!
    logMessage(DEBUG, "service close %d done", sock);
}

static Response calculater(const Request &req)
{
    Response resp;
    switch (req.op_)
    {
        case '+':
            resp.result_ = req.x_ + req.y_;
            break;
        case '-':
            resp.result_ = req.x_ - req.y_;
            break;
        case '*':
            resp.result_ = req.x_ * req.y_;
            break;
        case '/':
            {
                if(req.y_ != 0)
                    resp.result_ = req.x_ / req.y_;
                else
                    resp.exitCode_ = -1;// -1表示除零错误
            }
            break;
        case '%':
            {
                if(req.y_ != 0)
                    resp.result_ = req.x_ % req.y_;
                else
                    resp.exitCode_ = -2;// -2 表示 模零错误
            }
            break;
        default:
            resp.exitCode_ = -3; //表示 非法操作符
            break;
    }

    return resp;
}

// 1.全部手写
void netCal(int sock, const std::string &clientIp, uint16_t clientPort)
{
    assert(sock >= 0);
    assert(!clientIp.empty());
    assert(clientPort >= 1024);

    std::string inbuffer;
    // 9\r\n100 + 200\r\n
    while (true)
    {
        Request req;
        char buff[128];
        ssize_t s = read(sock, buff, sizeof(buff) - 1);
        if (s == 0)
        {
            logMessage(NOTICE, "client[%s:%d] close sock,service done", clientIp.c_str(), clientPort);
        }
        else if (s < 0)
        {
            logMessage(WARINING, "read client[%s:%d]error,errorcode:%d,errormessage:%s ",
                       clientIp.c_str(), clientPort, errno, strerror(errno));
            break;
        }

        // read success
        buff[s] = 0;
        inbuffer += buff;
        // 1.检查inbuffer是不是已经具有了一个strPackage
        uint32_t packageLen = 0;
        std::string packge = decode(inbuffer, &packageLen);//TODO
        if (packageLen == 0)
            continue; // 无法提取一个完整的报文
        // 2.已经获得一个完整的package
        if (req.deserialize(packge))
        {
            req.debug();
            // 3. 处理逻辑,输入的是一个req,得到了一个resp
            Response resp = calculater(req);
            // resp是一个结构化的数据
            //4. 对resp进行序列化
            std::string respPackage;
            resp.serialize(&respPackage);

            //5.对报文进行encode //TODO
            respPackage = encode(respPackage,respPackage.size());

            //6.简单进行发送 --
            write(sock,respPackage.c_str(),respPackage.size());
        }
    }
}

class ServerTcp
{
public:
    ServerTcp(uint16_t port, const std::string &ip = "")
        : port_(port), ip_(ip), listenSock_(-1), tp_(nullptr)
    {
        quit_ = false;
    }
    ~ServerTcp()
    {
        if (listenSock_ >= 0)
            close(listenSock_);
    }

public:
    void init()
    {
        // 1.创建套接字
        listenSock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listenSock_ < 0)
        {
            logMessage(FATAL, "socket: %s", strerror(errno));
            exit(SOCKET_ERR);
        }

        logMessage(DEBUG, "socket: %s,%d", strerror(errno), listenSock_);

        // 2.bind绑定
        // 2.1 填充服务器信息
        struct sockaddr_in local;         // 用户栈
        memset(&local, 0, sizeof(local)); // 清空
        local.sin_family = PF_INET;       // 进行网络通信
        local.sin_port = htons(port_);
        ip_.empty() ? (local.sin_addr.s_addr = INADDR_ANY)
                    : (inet_aton(ip_.c_str(), &local.sin_addr));
        // 2.2 将本地socket信息写入sock_对应的内核区域
        if (bind(listenSock_, (const struct sockaddr *)&local, sizeof local) < 0)
        {
            logMessage(FATAL, "bind: %s", strerror(errno));
            exit(BIND_ERR);
        }
        logMessage(DEBUG, "bind: %s,%d", strerror(errno), listenSock_);

        // 3.设置listen(监听状态)
        // 为何要监听呢？因为tcp是面向连接的!
        // 要被连接 服务器必须一直等待被别人连接
        if (listen(listenSock_, 5 /*后面再说*/) < 0) // == -1
        {
            logMessage(FATAL, "listen: %s", strerror(errno));
            exit(LISTEN_ERR);
        }
        logMessage(DEBUG, "listen: %s,%d", strerror(errno), listenSock_);
        // 允许别人来连接我了

        // 4.加载线程池
        tp_ = ThreadPool<Task>::getInstance();
    }

    // // 线程分离
    // static void *threadRoutine(void *args) // 类内方法 带有this指针
    // {
    //     pthread_detach(pthread_self()); // 将自己设置线程分离
    //     ThreadData *td = static_cast<ThreadData *>(args);
    //     td->this_->transService(td->sock_, td->clinetIp_, td->clientPort_);
    //     delete td;
    //     return nullptr;
    // }

    void start()
    {
        tp_->start();
        logMessage(DEBUG, "thread pool start success....thread num :%d", tp_->threadNum());
        while (!quit_)
        {

            // signal(SIGCHLD,SIG_IGN);//忽略SIGCHLD信号
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            // 4.获取链接 accept的返回值是一个新得socket fd ??
            // 4.1  serviceSock:给用户提供新的socket服务
            // 返回值的socket的主要是为用户提供网络服务的socket,主要是进行io
            int serviceSock = accept(listenSock_, (struct sockaddr *)&peer, &len);
            if (serviceSock < 0)
            {
                // 获取链接失败
                logMessage(WARINING, "accept:%s[%d]", strerror(errno), serviceSock);
                continue;
            }

            // //4.1 获取客户端基本信息
            uint16_t peerPort = ntohs(peer.sin_port);
            std::string peerIp = inet_ntoa(peer.sin_addr);

            logMessage(DEBUG, "accept: %s | %s[%d],socket fd:%d",
                       strerror(errno), peerIp.c_str(), peerPort, serviceSock);
            // logMessage(DEBUG, "server 提供 server start.........");
            // sleep(10);

            // 5. 提供服务
            // 5.1 单进程版 -- 以单价进入transService 主执行流就无法进行向后执行
            // transService(serviceSock,peerIp,peerPort);

            // // 5.2 父进程打开的文件会被子进程继承
            // pid_t id = fork();
            // assert(id != -1);
            // if(id == 0)
            // {
            //     close(listenSock_);//建议
            //     //子进程
            //     transService(serviceSock,peerIp,peerPort);
            //     exit(0);//进入僵尸
            // }
            // //父进程
            // close(serviceSock);//这一步一定要做的!
            // // waitpid();//默认是阻塞! WNOHANG
            // // 方案一: SIGCHLD

            // // 5.1 v2版本 方案二 -- 多进程版本  也是可以的
            // //爷爷进程
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     //爸爸进程
            //     close(listenSock_);//建议
            //     // 又进行了一次fork
            //     // 如果fork成功 爸爸进程直接退出了
            //     if(fork()>0) exit(0);
            //     // 孙子进程就是没有爸爸了 -- 孙子进程就成了孤儿进程 会被系统领养 回收问题就会交给系统回收
            //     transService(serviceSock,peerIp,peerPort);
            //     exit(0);
            // }
            // //父进程
            // close(serviceSock);//这一步一定要做的!
            // //爸爸进程直接终止，立马得到退出码，释放僵尸进程状态
            // pid_t ret = waitpid(id,nullptr,0);//就用阻塞式
            // assert(ret >0 );
            // (void) ret;

            // // 5.2 v2版本 -- 多线程
            // //线程数据
            // ThreadData *td = new ThreadData(peerPort,peerIp,serviceSock,this);
            // //这里不需要进行关闭文件描述符吗 ？ 不需要 因为线程是会共享文件描述符的
            // pthread_t tid;
            // pthread_create(&tid,nullptr,threadRoutine,(void*)td);

            // // 5.3 v3版本 线程池技术 --  引入线程池
            // // 5.3.1 构建任务
            // Task t(serviceSock, peerIp, peerPort, std::bind(&ServerTcp::transService, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            // tp_->push(t);

            // 5.3
            // v3.2
            // Task t(serviceSock,peerIp,peerPort,transService);
            // tp_->push(t);
            // 5.3 v3.3
            // Task t(serviceSock, peerIp, peerPort, execCommand);
            // tp_->push(t);

            // 5.4
            Task t(serviceSock, peerIp, peerPort, netCal);
            tp_->push(t);
        }
    }

    bool quitServer()
    {
        quit_ = true;
    }

private:
    int listenSock_;
    uint16_t port_;
    std::string ip_;
    // 引入线程池
    ThreadPool<Task> *tp_;

    // 安全退出
    bool quit_;
};

static void Usage(std::string proc)
{
    std::cerr << "Usage:\n\t" << proc << " port ip" << std::endl;
    std::cerr << "example:\n\t" << proc << " 8080 127.0.0.1 \n"
              << std::endl;
}

ServerTcp *svrp = nullptr;

void sighandler(int signo)
{
    if (signo == 3 && svrp != nullptr)
        svrp->quitServer();
    logMessage(DEBUG, "server quit save ! ");
}

// ./ServerTcp local_port local_ip
int main(int argc, char *argv[])
{
    if (argc != 2 && argc != 3)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);
    std::string ip;
    if (argc == 3)
        ip = argv[2];

    // demonize(); // 让我们这个进程成为守护进程
    signal(3, sighandler);
    // Log log;
    // log.enable();
    ServerTcp svr(port, ip);
    svr.init();
    svrp = &svr;
    svr.start();

    return 0;
}